class PieChart {
	scaleX = 0;
	scaleY = 0;
	originX = 0;
	originY = 0;
	radius = 200;
	canvas = null;
	ctx = null;
	data = [];
	AngleData = [];
	total = 1;
	startAngle = 0;

	constructor(config) {
		const {
			origin: {
				x,
				y
			},
			radius
		} = config;
		this.scaleX = x;
		this.scaleY = y;
		this.radius = radius;
		this.data = config.series[0].data;

		this.get2D();
		this.init();
		this.draw();
	}

	// 初始化
	init() {
		// 计算出圆点坐标
		this.originX = this.canvas.width * this.scaleX;
		this.originY = this.canvas.height * this.scaleY;
		// 计算出总数
		for (let i = 0; i < this.data.length; i++) {
			this.total += this.data[i].value;
		}
		// 监听canvas元素的鼠标事件
		this.canvas.addEventListener('mousemove', this.onMouseMove.bind(this));
	}

	// 获取Canvas2D对象
	get2D() {
		this.canvas = document.querySelector('#canvas');
		this.ctx = this.canvas.getContext('2d')
	}

	// 绘制完整圆形
	draw() {
		let startAngle = this.startAngle;
		for (let i = 0; i < this.data.length; i++) {
			const item = this.data[i];
			item.color = item.color ? item.color : this.randomColor();
			const radian = (item.value / this.total) * Math.PI * 2;
			const endAngle = startAngle + radian;
			const middleAngle = (startAngle + endAngle) / 2;
			// 记录角度数据，用于显示tooltip
			this.AngleData.push({
				start: startAngle,
				middle: middleAngle,
				end: endAngle
			})
			this.drawPie(startAngle, endAngle, item.color);
			this.drawLine(middleAngle, item.color);
			this.drawPieText(item.name, middleAngle)
			startAngle = endAngle;
		}
	}

	// 绘制扇形
	drawPie(startAngle, endAngle, color, hover = false) {
		this.ctx.save();
		this.ctx.beginPath();
		this.ctx.moveTo(this.originX, this.originY)
		this.ctx.fillStyle = color;
		// 如果被选择，设置阴影
		if(hover) {
			this.ctx.shadowColor = '#999';
			this.ctx.shadowBlur = 10;
		}
		this.ctx.arc(this.originX, this.originY, this.radius + (hover ? 3 : 0), startAngle, endAngle, false)
		this.ctx.fill();
		this.ctx.restore();
	}

	// 绘制指向pie的文字，以圆心作为相对坐标原点
	drawPieText(text, angle) {
		// 文字和图形的间距
		let gap = 50;
		let textWidth = this.ctx.measureText(text).width;
		let textHeight = this.ctx.font.match(/(\d+)px/)[1];
		// 左右都是减
		// 上下都是增
		// 文字的原点在左下角
		const x = this.originX + (this.radius + gap) * Math.cos(angle) - textWidth / 2;
		const y = this.originY + (this.radius + gap) * Math.sin(angle) + textHeight / 2;
		this.ctx.fillStyle = '#000'
		this.ctx.fillText(text, x, y);
	}

	// 绘制链接pie和文字的线条
	drawLine(angle, color) {
		// 线条和pie的间距
		let gap = 5;
		let length = 30
		const startX = this.originX + (this.radius + gap) * Math.cos(angle);
		const startY = this.originY + (this.radius + gap) * Math.sin(angle);
		const endX = this.originX + (this.radius + gap + length) * Math.cos(angle);
		const endY = this.originY + (this.radius + gap + length) * Math.sin(angle);
		this.ctx.beginPath();
		this.ctx.strokeStyle = color;
		this.ctx.moveTo(startX, startY);
		this.ctx.lineTo(endX, endY);
		this.ctx.stroke();
	}

	onMouseMove(event) {
		const offsetX = event.offsetX;
		const offsetY = event.offsetY;
		const distanceX = offsetX - this.originX;
		const distanceY = offsetY - this.originY;

		if (this.isInCircle(Math.abs(distanceX), Math.abs(distanceY))) {
			// 更改光标图案
			this.canvas.style.cursor = "pointer";
			const atan2Value = Math.atan2(distanceY, distanceX);
			let angle = atan2Value;
			if (atan2Value < 0) {
				angle = Math.PI * 2 + atan2Value;
			}

			let itemIndex = 0;
			for (let i = 0; i < this.AngleData.length; i++) {
				const item = this.AngleData[i];
				if (item.start <= angle && item.end > angle) {
					itemIndex = i;
					break;
				}
			}
			// 清空画布
			this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
			// 重绘
			this.draw();
			this.drawPie(this.AngleData[itemIndex].start, this.AngleData[itemIndex].end, this.data[itemIndex].color, true);
			this.showTooltip(this.data[itemIndex], offsetX + 15, offsetY + 10)
		} else {
			// 恢复光标图案
			this.canvas.style.cursor = 'default';
			this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
			this.draw();
		}
	}

	// 显示提示文字
	showTooltip(data, x, y) {
		// 画一个带文字的矩形
		let maxTextWidth = 0;
		const textConfig = {
			borderColor: data.color,
			pointColor: data.color,
			pointRadius: 5,
			fontSize: 16,
			padding: 10,
			data: []
		};
		textConfig.data.push(data.name)
		textConfig.data.push(data.value)

		// 求最大文字宽度
		for (let i = 0; i < textConfig.data.length; i++) {
			const length = this.ctx.measureText(textConfig.data[i].toString()).width;
			if (length > maxTextWidth) {
				maxTextWidth = length;
			}
		}

		const boxWidth = maxTextWidth + textConfig.padding * 2;
		const boxHeight = textConfig.data.length * (textConfig.fontSize + textConfig.padding * 2) - textConfig.padding + textConfig.pointRadius * 2;
		this.generateRectWithText(x, y, boxWidth, boxHeight, textConfig);
	}

	// 生成一个带文字的矩形
	generateRectWithText(x, y, width, height, textConfig = {}) {
		width = width > 120 ? width : 120;
		this.ctx.save();
		this.ctx.beginPath();
		// 绘制图形
		this.ctx.fillStyle = '#FFF';
		this.ctx.fillRect(x, y, width, height);
		this.ctx.lineWidth = 0.5;
		this.ctx.strokeStyle = textConfig.borderColor;
		this.ctx.strokeRect(x, y, width, height);
		this.ctx.fillStyle = textConfig.pointColor;
		this.ctx.arc(x + textConfig.padding + textConfig.pointRadius, y + textConfig.padding + textConfig.pointRadius, textConfig.pointRadius, 0, Math.PI * 2, false);
		this.ctx.fill();

		// 绘制文字
		this.ctx.fillStyle = "#000";
		const textBaseX = x + textConfig.padding * 2 + textConfig.pointRadius * 2;
		const textBaseY = y + textConfig.padding * 2 + textConfig.pointRadius * 2;
		this.ctx.font = `${textConfig.fontSize}px sans-serif`;
		for (let i = 0; i < textConfig.data.length; i++) {
			const item = textConfig.data[i];
			this.ctx.fillText(item, textBaseX, textBaseY + i * (textConfig.padding + textConfig.fontSize));
		}
		this.ctx.restore();
	}

	// 随机生成颜色值
	randomColor() {
		return `#${this.dec2hex()}${this.dec2hex()}${this.dec2hex()}`;
	}

	// 十进制转16进制
	dec2hex(decNum = 256) {
		return Math.ceil(Math.random() * decNum).toString(16);
	}

	// 判断是否在圆内
	isInCircle(distanceX, distanceY) {
		return distanceX * distanceX + distanceY * distanceY <= this.radius * this.radius;
	}
}